#import "FoursquareVenueCollection.h"

// Shared instance of the Foursquare venue collection
static FoursquareVenueCollection* sharedVenueCollection = nil;

//  This Foursquare integration is possible thanks to the Foursquare API by
//  Eric Butler and associates.
//
//  Copyright (C) 2010 Eric Butler <eric@codebutler.com>

/**
 * A FoursquareVenueCollection is an object representing a collection
 * of venues in the Foursquare system.
 *
 * Provides the functionality to perform necessary queries on the set of 
 * venues.
 *
 * Code used within this class and the supporting API was written by 
 * Eric Butler and other associates.
 *
 * The supporting API can be found in ./FourSquareAPI folder.
 *
 * @author      Eric Butler
 * @author      James Lin
 * @version     2.3.3 
 * @since       2.1.3
 */
@implementation FoursquareVenueCollection

/**
 * Retrieve the shared instance of the Foursquare venue collection.
 *
 * @return  the shared instance of FoursquareVenueCollection
 */
+ (FoursquareVenueCollection*)sharedVenueCollection {
	@synchronized (self) {
		if (sharedVenueCollection == nil) {
			sharedVenueCollection = [[FoursquareVenueCollection alloc] init];
		}
		return sharedVenueCollection;
	}
}

/**
 * Allocate the Foursquare venue collection.
 *
 * @return  the newly allocated shared instance of FoursquareVenueCollection
 */
+ (id)alloc {
	@synchronized (self) {
		sharedVenueCollection = [super alloc];
		return sharedVenueCollection;
	}
}

/**
 * Retrieve a copy of the Foursquare venue collection.
 *
 * @return  the shared instance of FoursquareVenueCollection
 */
+ (id)copy {
	@synchronized (self) {
		return sharedVenueCollection;
	}
}

/**
 * Initialize the Foursquare venue collection.
 *
 * @return  the initialized FoursquareVenueCollection
 */
- (id)init {
    if (self = [super init]) {
		venueCollection = [[NSMutableArray array] retain];
    }
    return self;
}

/**
 * Get the number of venues in the collection.
 *
 * @return  the number of venues in the collection
 */
- (NSInteger)size {
	return [venueCollection count];
}

/**
 * Get the list of venues.
 *
 * @return  the list of venues
 */
- (NSArray*)allVenues {
	return venueCollection;
}

/**
 * Helper method to store a venue into this collection.
 *
 * @param venue    the dictionary containing the venue information
 * @param loadAll  whether to additionally retrieve remaining venue info
 */
- (void)storeVenue:(NSDictionary*)venue andLoadAll:(BOOL)loadAll {
	// Get all the attributes
	NSString* identifier = [venue objectForKey:@"id"];
	NSString* name = [venue objectForKey:@"name"];
	NSString* address = [venue objectForKey:@"address"];
	NSString* city = [venue objectForKey:@"city"];
	NSString* distance = [venue objectForKey:@"distance"];
	NSString* category = [[venue objectForKey:@"primarycategory"] objectForKey:@"nodename"];
	NSString* latitude = [venue objectForKey:@"geolat"];
	NSString* longitude = [venue objectForKey:@"geolong"];
	NSDictionary* specials = [venue objectForKey:@"specials"];
	
	// Build the empty venue container and add it to the collection
	FoursquareVenue* tempVenue = [FoursquareVenue alloc];
	[tempVenue initWithID:identifier 
					 name:name
				  address:address
					 city:city 
				 distance:distance
				 category:category 
				 latitude:latitude 
				longitude:longitude 
				 specials:specials];
	
	if ([category length] > 0) {
		NSString* url = [[venue objectForKey:@"primarycategory"] objectForKey:@"iconurl"];
		tempVenue.venueImageURL = url;
	}
	
	if (loadAll) {
		[tempVenue loadExtraInfo];
	}
	
	/*if (specials) {
		NSLog(@"Specials at %@: %@", name, specials);
	}*/
	
	[venueCollection addObject:tempVenue];
	[tempVenue release];
}

/**
 * Retrieve a fresh set of nearby venue data.
 *
 * Currently only supports a maximum of 50 venues as stated by the API.
 *
 * @param location     the user location
 * @param theDelegate  the delegate handling update notification
 * @param venueLimit   the maximum number of venues to find
 * @param identifier   the unique identifier for this update request
 */
- (void)updateWithUserLocation:(CLLocation*)location 
				   andDelegate:(id<FoursquareVenueCollectionDelegate>)theDelegate 
					venueLimit:(NSUInteger)venueLimit 
					identifier:(NSUInteger)identifier {
	if (!location) {
		return;
	}
	
	NSString* lat = [NSString stringWithFormat:@"%f", location.coordinate.latitude];
	NSString* lon = [NSString stringWithFormat:@"%f", location.coordinate.longitude];
	delegate = theDelegate;
	
	[Foursquare venuesNearLatitude:lat 
						 longitude:lon
						  matching:nil 
							 limit:[NSNumber numberWithInt:venueLimit] 
						  callback:
	 ^(BOOL success, id response) {
		 if (success) {
			 // Clear the collection first
			 [venueCollection removeAllObjects];
			 
			 // Traverse down to the venue array
			 NSDictionary* venuesDict = [response objectForKey:@"venues"];
			 id groupItem = [venuesDict objectForKey:@"group"];
			 NSArray* venues;
			 
			 if ([groupItem isKindOfClass:[NSArray class]]) {
				 venues = groupItem;
			 } else {
				 NSDictionary* groupDict = (NSDictionary*)groupItem;
				 venues = [groupDict objectForKey:@"venue"];
			 }
			 
			 // Add each venue to the collection
			 if ([groupItem isKindOfClass:[NSArray class]]) {
				 for (id venuesValue in venues) {
					 NSDictionary* group = (NSDictionary*)venuesValue;
					 if ([[group objectForKey:@"type"] isEqualToString:@"Nearby"]) {
						 NSArray* newVenues = [group objectForKey:@"venue"];
						 for (id venueValue in newVenues) {
							 NSDictionary* venue = (NSDictionary*)venueValue;
							 [self storeVenue:venue andLoadAll:NO];
						 }
					 }
				 }
			 } else { 
				 for (id value in venues) {
					 NSDictionary* venue = (NSDictionary*)value;
					 [self storeVenue:venue andLoadAll:NO];
				 }
			 }
			 
			 if (delegate) {
				 [delegate handleVenueUpdateDoneWithIdentifier:identifier];
			 }
		 }
	 }];
}

/**
 * Find a fresh set of nearby venue specials.
 *
 * @param location       the user location
 * @param theDelegate    the delegate handling search notification
 * @param specialsLimit  the maximum number of specials to find
 */
- (void)findSpecialsWithUserLocation:(CLLocation*)location 
						 andDelegate:(id<FoursquareVenueCollectionDelegate>)theDelegate 
					   specialsLimit:(NSUInteger)specialsLimit {
	if (!location) {
		return;
	}
	
	delegate = theDelegate;
	
	// Initiate 5 venue searches in a cross formation to attempt to retrieve more specials
	for (int i = 4; i >= 0; i--) {
		double latOffset = 0.0;
		double lonOffset = 0.0;
		
		switch (i) {
			case 0:
				break;
			case 1:
				latOffset = -0.005;
				break;
			case 2:
				latOffset = 0.005;
				break;
			case 3:
				lonOffset = -0.005;
				break;
			case 4:
				lonOffset = 0.005;
				break;
			/*case 5:
				latOffset = -0.010;
				break;
			case 6:
				latOffset = 0.010;
				break;
			case 7:
				lonOffset = -0.010;
				break;
			case 8:
				lonOffset = 0.010;
				break;
			case 9:
				lonOffset = -0.015;
				break;
			case 10:
				lonOffset = 0.015;
				break;*/
		}
		
		NSString* lat = [NSString stringWithFormat:@"%f", 
						 location.coordinate.latitude + latOffset];
		NSString* lon = [NSString stringWithFormat:@"%f", 
						 location.coordinate.longitude + lonOffset];
		
		[Foursquare venuesNearLatitude:lat 
							 longitude:lon
							  matching:nil 
								 limit:[NSNumber numberWithInt:50] 
							  callback:
		 ^(BOOL success, id response) {
			 if (success) {
				 // Clear the collection first
				 [venueCollection removeAllObjects];
				 
				 // Traverse down to the venue array
				 NSDictionary* venuesDict = [response objectForKey:@"venues"];
				 id groupItem = [venuesDict objectForKey:@"group"];
				 NSArray* venues;
				 
				 //NSLog(@"\n\n\n\n%@", groupItem);
				 
				 if ([groupItem isKindOfClass:[NSArray class]]) {
					 venues = groupItem;
				 } else {
					 NSDictionary* groupDict = (NSDictionary*)groupItem;
					 venues = [groupDict objectForKey:@"venue"];
				 }
				 
				 // Add each venue to the collection
				 if ([groupItem isKindOfClass:[NSArray class]]) {
					 for (id venuesValue in venues) {
						 NSDictionary* group = (NSDictionary*)venuesValue;
						 if ([[group objectForKey:@"type"] isEqualToString:@"Nearby"]) {
							 NSArray* newVenues = [group objectForKey:@"venue"];
							 for (id venueValue in newVenues) {
								 NSDictionary* venue = (NSDictionary*)venueValue;
								 [self storeVenue:venue andLoadAll:NO];
							 }
						 }
					 }
				 } else { 
					 for (id value in venues) {
						 NSDictionary* venue = (NSDictionary*)value;
						 [self storeVenue:venue andLoadAll:NO];
					 }
				 }			 
			 }
			 
			 // Proceed to extract specials from the venue results
			 if (i == 0) {
				 // Create container for venues with specials
				 NSMutableArray* specials = [NSMutableArray array];
				 
				 for (FoursquareVenue* venue in venueCollection) {
					 if (![specials containsObject:venue]) {
						 if (venue.venueSpecials && [venue.venueSpecials count] > 0) {
							 //NSLog(@"Venue \"%@\" has a special.", venue.venueName);
							 [specials addObject:venue];
						 }
					 }
				 }
				 
				 if (delegate) {
					 [delegate handleVenueSpecialsResults:specials];
				 }
			 }
		 }];
	}
}

/**
 * Find the venue given a list of possible venue names.
 *
 * @param name  possible names of venue to find
 * @param       the Foursquare if found;
 *              nil otherwise
 */
- (FoursquareVenue*)findVenueWithNames:(NSArray*)names {
	for (FoursquareVenue* venue in venueCollection) {
		for (NSString* name in names) {
			// Remove all periods
			NSString* tmpName = venue.venueName;
			tmpName = [tmpName stringByReplacingOccurrencesOfString:@"." withString:@" "];
			
			// Compare names
			NSRange matchRange;
			matchRange = [name rangeOfString:tmpName options:NSCaseInsensitiveSearch];
			
			if (matchRange.location != NSNotFound) {
				return venue;
			}
		}
	}
	
	return nil;
}

/**
 * Deallocate the shared Foursquare venue collection.
 */
- (void)dealloc {
	delegate = nil;
	[venueCollection release], venueCollection = nil;
	[sharedVenueCollection release], sharedVenueCollection = nil;
	[super dealloc];
}

@end
